var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
import { findAndRemoveImports, hasImports, getFileCategory, openApiCheck } from '@mintlify/common';
import { createPage, preparseMdx } from '@mintlify/prebuild';
import { formatIssue, validateMintConfig } from '@mintlify/validation';
import Chalk from 'chalk';
import chokidar from 'chokidar';
import { promises as _promises } from 'fs';
import fse from 'fs-extra';
import pathUtil from 'path';
import { CMD_EXEC_PATH, NEXT_PROPS_PATH, NEXT_PUBLIC_PATH } from '../../constants.js';
import { generatePagesWithImports } from './generatePagesWithImports.js';
import { resolveAllImports } from './resolveAllImports.js';
import { updateGeneratedNav, updateOpenApiFiles } from './update.js';
import { isFileSizeValid } from './utils.js';
const { readFile } = _promises;
const listener = (callback) => {
    chokidar
        .watch(CMD_EXEC_PATH, {
        ignoreInitial: true,
        ignored: ['node_modules', '.git', '.idea'],
        cwd: CMD_EXEC_PATH,
    })
        .on('add', (filename) => onAddEvent(filename, callback))
        .on('change', (filename) => onChangeEvent(filename, callback))
        .on('unlink', onUnlinkEvent);
};
const onAddEvent = (filename, callback) => __awaiter(void 0, void 0, void 0, function* () {
    try {
        const category = yield onUpdateEvent(filename, callback);
        switch (category) {
            case 'page':
                console.log('New page detected: ', filename);
                break;
            case 'snippet-v2':
            case 'snippet':
                console.log('New snippet detected: ', filename);
                break;
            case 'mintConfig':
                console.log('Config added');
                break;
            case 'openApi':
                console.log('OpenApi spec added: ', filename);
                break;
            case 'css':
                console.log('CSS file added: ', filename);
                break;
            case 'js':
                console.log('JS file added: ', filename);
                break;
            case 'staticFile':
                console.log('Static file added: ', filename);
                break;
        }
    }
    catch (error) {
        console.error(error.message);
    }
});
const onChangeEvent = (filename, callback) => __awaiter(void 0, void 0, void 0, function* () {
    try {
        const category = yield onUpdateEvent(filename, callback);
        switch (category) {
            case 'page':
                console.log('Page edited: ', filename);
                break;
            case 'snippet-v2':
            case 'snippet':
                console.log('Snippet edited: ', filename);
                break;
            case 'mintConfig':
                console.log('Config edited');
                break;
            case 'openApi':
                console.log('OpenApi spec edited: ', filename);
                break;
            case 'css':
                console.log('CSS file edited: ', filename);
                break;
            case 'js':
                console.log('JS file edited: ', filename);
                break;
            case 'staticFile':
                console.log('Static file edited: ', filename);
                break;
        }
    }
    catch (error) {
        console.error(error.message);
    }
});
const onUnlinkEvent = (filename) => __awaiter(void 0, void 0, void 0, function* () {
    try {
        const potentialCategory = getFileCategory(filename);
        const targetPath = getTargetPath(potentialCategory, filename);
        if (potentialCategory === 'page' ||
            potentialCategory === 'snippet' ||
            potentialCategory === 'mintConfig' ||
            potentialCategory === 'staticFile' ||
            potentialCategory === 'snippet-v2' ||
            potentialCategory === 'css' ||
            potentialCategory === 'js') {
            yield fse.remove(targetPath);
        }
        switch (potentialCategory) {
            case 'page':
                console.log(`Page deleted: ${filename}`);
                break;
            case 'snippet-v2':
            case 'snippet':
                console.log(`Snippet deleted: ${filename}`);
                break;
            case 'mintConfig':
                console.error('⚠️ mint.json is required, but has been deleted. Exiting.');
                process.exit(1);
            case 'potentialJsonOpenApiSpec':
            case 'potentialYamlOpenApiSpec':
                yield updateOpenApiFiles();
                yield updateGeneratedNav();
                break;
            case 'css':
                console.log(`CSS file deleted: ${filename}`);
                break;
            case 'js':
                console.log(`JS file deleted: ${filename}`);
                break;
            case 'staticFile':
                console.log('Static file deleted: ', filename);
                break;
        }
    }
    catch (error) {
        console.error(error.message);
    }
});
const getTargetPath = (potentialCategory, filePath) => {
    switch (potentialCategory) {
        case 'page':
            return pathUtil.join(NEXT_PROPS_PATH, filePath);
        case 'mintConfig':
            return pathUtil.join(NEXT_PROPS_PATH, 'mint.json');
        case 'potentialYamlOpenApiSpec':
        case 'potentialJsonOpenApiSpec':
            return pathUtil.join(NEXT_PROPS_PATH, 'openApiFiles.json');
        case 'snippet':
        case 'staticFile':
        case 'snippet-v2':
        case 'css':
        case 'js':
            return pathUtil.join(NEXT_PUBLIC_PATH, filePath);
        default:
            throw new Error('Invalid category');
    }
};
/**
 * This function is called when a file is added or changed
 * @param filename
 * @returns FileCategory
 */
const onUpdateEvent = (filename, callback) => __awaiter(void 0, void 0, void 0, function* () {
    const filePath = pathUtil.join(CMD_EXEC_PATH, filename);
    const potentialCategory = getFileCategory(filename);
    const targetPath = getTargetPath(potentialCategory, filename);
    let regenerateNav = false;
    let category = potentialCategory === 'potentialYamlOpenApiSpec' ||
        potentialCategory === 'potentialJsonOpenApiSpec'
        ? 'staticFile'
        : potentialCategory;
    switch (potentialCategory) {
        case 'page': {
            regenerateNav = true;
            let contentStr = (yield readFile(filePath)).toString();
            const importsResponse = yield findAndRemoveImports(contentStr);
            if (hasImports(importsResponse)) {
                contentStr = yield resolveAllImports(Object.assign(Object.assign({}, importsResponse), { filename }));
            }
            const { pageContent } = yield createPage(filename, contentStr, CMD_EXEC_PATH, []);
            yield fse.outputFile(targetPath, pageContent, {
                flag: 'w',
            });
            break;
        }
        case 'snippet':
            yield fse.copy(filePath, targetPath);
            break;
        case 'snippet-v2': {
            let contentStr = (yield readFile(filePath)).toString();
            const importsResponse = yield findAndRemoveImports(contentStr);
            contentStr = yield preparseMdx(contentStr, CMD_EXEC_PATH, filePath);
            if (hasImports(importsResponse)) {
                contentStr = yield resolveAllImports(Object.assign(Object.assign({}, importsResponse), { filename }));
            }
            yield fse.outputFile(targetPath, contentStr, {
                flag: 'w',
            });
            yield generatePagesWithImports();
            break;
        }
        case 'mintConfig':
            regenerateNav = true;
            const mintJsonFileContent = (yield readFile(filePath)).toString();
            try {
                const configObj = JSON.parse(mintJsonFileContent);
                const validationResults = validateMintConfig(configObj);
                if (validationResults.success) {
                    if (validationResults.warnings.length > 0) {
                        console.warn(Chalk.yellow('⚠️ Warnings found in mint.json:'));
                        validationResults.warnings.forEach((issue) => console.warn(Chalk.yellow(formatIssue(issue))));
                    }
                    yield fse.copy(filePath, targetPath);
                }
                else {
                    console.error(Chalk.red('🚨 Invalid mint.json:'));
                    validationResults.error.issues.forEach((issue) => console.error(Chalk.red(formatIssue(issue))));
                }
            }
            catch (e) {
                const error = e;
                if (error.name === 'SyntaxError') {
                    console.error(`🚨 ${Chalk.red('mint.json has invalid JSON. You are likely missing a comma or a bracket. You can paste your mint.json file into https://jsonlint.com/ to get a more specific error message.')}`);
                }
                else {
                    console.error(`🚨 ${Chalk.red(error.message)}`);
                }
            }
            break;
        case 'potentialYamlOpenApiSpec':
        case 'potentialJsonOpenApiSpec':
            const doc = yield openApiCheck(filePath);
            if (doc) {
                // TODO: Instead of re-generating all openApi files, optimize by just updating the specific file that changed.
                yield updateOpenApiFiles();
                regenerateNav = true;
                category = 'openApi';
            }
            break;
        case 'css':
        case 'js':
        case 'staticFile':
            if (yield isFileSizeValid(filePath, 5)) {
                yield fse.copy(filePath, targetPath);
            }
            else {
                console.error(Chalk.red(`🚨 The file at ${filename} is too big. The maximum file size is 5 mb.`));
            }
            break;
    }
    if (regenerateNav) {
        // TODO: Instead of re-generating the entire nav, optimize by just updating the specific page that changed.
        yield updateGeneratedNav();
    }
    callback();
    return category;
});
export default listener;
